<?php
/**
 * Created by PhpStorm.
 * User: syzhou
 * Date: 2019-08-24
 * Time: 21:09
 */

namespace App\Services;


use App\WorkflowPhase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class WorkflowService
{
    const PHASE_APPLY = 'A';
    const PHASE_FORMAL_REVIEW = 'B';
    const PHASE_ASSIGN_REVIEW = 'C';
    const PHASE_EXPERT_REVIEW = 'D';
    const PHASE_MEETING_REVIEW = 'E';
    const PHASE_PLAN_AWARD = 'F';  // 管理员设置授奖等级
    const PHASE_VOTE = 'G';
    const PHASE_PREPARE_DEFENSE = 'H' ;  // 准备答辩，即选择参加二次会评的项目
    const PHASE_DEFENSE = 'I';  // 答辩
    const PHASE_AWARD = 'J';  // 授奖

    // 废弃的阶段
    const PHASE_PLAN_AWARD_2 = 'Y'; // 大组长划定待定区
    const PHASE_VOTE_1 = 'Z';  // 待定区投票

    private $phases = null;  // array, phase_id => phase(stdClass)

    const PHASE_TRANSFER = [
        self::PHASE_APPLY => self::PHASE_FORMAL_REVIEW,
        self::PHASE_FORMAL_REVIEW => self::PHASE_ASSIGN_REVIEW,
        self::PHASE_ASSIGN_REVIEW => self::PHASE_EXPERT_REVIEW,
        self::PHASE_EXPERT_REVIEW => self::PHASE_MEETING_REVIEW,
        self::PHASE_MEETING_REVIEW => self::PHASE_PLAN_AWARD,
        self::PHASE_PLAN_AWARD => self::PHASE_VOTE,
        self::PHASE_VOTE => self::PHASE_PREPARE_DEFENSE,
        self::PHASE_PREPARE_DEFENSE => self::PHASE_DEFENSE,
        self::PHASE_DEFENSE => self::PHASE_AWARD,
    ];

    /**
     * @throws \Exception
     */
    public function __construct()
    {
        $this->phases = DB::table('workflow_phases')->get()->toArray();
    }

    public function atPhase($phaseId, $largeGroupId = 0)
    {
        return $this->atPhases([$phaseId], $largeGroupId);
    }

    public function notAtPhase($phaseId, $largeGroupId = 0)
    {
        return $this->notAtPhases([$phaseId], $largeGroupId);
    }

    /**
     * 系统当前是否处在指定的状态之一
     * @param $phaseIds array
     * @param $largeGroupId int|string|null null|0 代表与大组无关
     * @return bool
     */
    public function atPhases($phaseIds, $largeGroupId = 0)
    {
        $largeGroupId = (int)$largeGroupId;
        foreach ($phaseIds as $phaseId) {
            $phase = $this->getPhase($phaseId, $largeGroupId);
            if ($phase && $phase->has_begun && !$phase->has_ended) {
                return true;
            }
        }
        return false;
    }

    /**
     * /**
     * 系统当前是否不处在指定状态的任何一个
     * @param $phaseIds array
     * @param $largeGroupId int|string|null null|0|'0' 代表与大组无关
     * @return bool
     */
    public function notAtPhases($phaseIds, $largeGroupId = 0)
    {
        return !$this->atPhases($phaseIds, $largeGroupId);
    }

    public function hasPhaseBegun($phaseId, $largeGroupId = 0)
    {
        $phase = $this->getPhase($phaseId, (int)$largeGroupId);
        return $phase && $phase->has_begun;
    }

    public function hasPhaseEnded($phaseId, $largeGroupId = 0)
    {
        $phase = $this->getPhase($phaseId, (int)$largeGroupId);
        return $phase && $phase->has_ended;
    }

    /**
     * @param $phaseId
     * @param $largeGroupId
     * @param $hasBegun bool|1|0
     * @param $hasEnded bool|1|0
     * @param bool $inTransaction
     * @return array
     */
    public function updatePhaseStatus($phaseId, $largeGroupId, $hasBegun, $hasEnded, $inTransaction = false) {
        $hasBegun = (int)$hasBegun;
        $hasEnded = (int)$hasEnded;
        $largeGroupId = (int)$largeGroupId;
        try {
            $phase = $this->getPhase($phaseId, $largeGroupId);
            if ($phase === null) {
                return [
                    'ok' => false,
                    'code' => 3,
                    'msg' => "找不到阶段 #$phaseId"
                ];
            }
            WorkflowPhase::where('phase_id', $phaseId)
                ->where('large_group_id', $largeGroupId)
                ->update(['has_begun' => $hasBegun, 'has_ended' => $hasEnded]);
            $phase->has_begun = $hasBegun;
            $phase->has_ended = $hasEnded;
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('[WorkflowService::beginPhase]', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误'
            ];
        }
    }

    /**
     * @param $phaseId
     * @param $largeGroupId  int|string|null null|0 代表与大组无关
     * @param $inTransaction bool 该方法被调用时，是否处在在数据库事务中
     * @return array 模式:
     * - ok: bool
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     * - msg: string
     */
    public function beginPhase($phaseId, $largeGroupId, $inTransaction = false)
    {
        $largeGroupId = (int)$largeGroupId;
        try {
            $phase = $this->getPhase($phaseId, $largeGroupId);
            if ($phase === null) {
                return [
                    'ok' => false,
                    'code' => 3,
                    'msg' => "找不到阶段 #$phaseId"
                ];
            }
            WorkflowPhase::where('phase_id', $phaseId)
                ->where('large_group_id', $largeGroupId)
                ->update(['has_begun' => 1,'has_ended' => 0]);
            $phase->has_begun = 1;
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('[WorkflowService::beginPhase]', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误'
            ];
        }
    }

    /**
     * @param $phaseId
     * @param $largeGroupId  int|string|null null|0 代表与大组无关
     * @param $inTransaction bool 该方法被调用时，是否处在在数据库事务中
     * @return array 模式:
     * - ok: bool
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     * - msg: string
     */
    public function endPhase($phaseId, $largeGroupId, $inTransaction = false)
    {
        $largeGroupId = (int)$largeGroupId;
        try {
            $phase = $this->getPhase($phaseId, $largeGroupId);
            WorkflowPhase::where('phase_id', $phaseId)
                ->where('large_group_id', $largeGroupId)
                ->update(['has_ended' => 1]);
            $phase->has_ended = 1;
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('[WorkflowService::endPhase]', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误'
            ];
        }
    }

    /**
     * @param $fromPhaseId
     * @param $toPhaseId
     * @param int $largeGroupId
     * @param bool $inTransaction 该方法被调用时，是否处在在数据库事务中
     * @return array
     */
    public function transferPhase($fromPhaseId, $toPhaseId, $largeGroupId = 0, $inTransaction = false)
    {
        $inNewTransaction = false;
        try {
            if (!$inTransaction) {
                DB::beginTransaction();
                $inNewTransaction = true;
            }
            $endPhaseResult = $this->endPhase($fromPhaseId, $largeGroupId, true);
            if (!$endPhaseResult['ok']) {
                if ($inNewTransaction) {
                    DB::rollBack();
                }
                return [
                    'ok' => true,
                    'code' => 3,
                    'msg' => "结束阶段 #$fromPhaseId 时出错(code={$endPhaseResult['code']}, msg={$endPhaseResult['msg']})",
                ];
            }
            $beginPhaseResult = $this->beginPhase($toPhaseId, $largeGroupId, true);
            if (!$beginPhaseResult['ok']) {
                if ($inNewTransaction) {
                    DB::rollBack();
                }
                return [
                    'ok' => true,
                    'code' => 4,
                    'msg' => "开始阶段 #$toPhaseId 时出错(code={$beginPhaseResult['code']}, msg={$beginPhaseResult['msg']}) ",
                ];
            }
            if ($inNewTransaction) {
                DB::commit();
            }
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            if ($inNewTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $e2) {

                }
            }
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误'
            ];
        }
    }

    /**
     * 从 $this->phases 中，找到指定 phase
     * @param $phaseId
     * @param $largeGroupId
     * @return \stdClass|null
     */
    private function getPhase($phaseId, $largeGroupId)
    {
        $largeGroupId = (int)$largeGroupId;
        foreach ($this->phases as $phase) {
            if ($phaseId == $phase->phase_id && $largeGroupId == $phase->large_group_id) {
                return $phase;
            }
        }
        return null;
    }


    public function getDescendingPhases($phaseId, $includeSelf = true) {
        $descendingPhases = $includeSelf ? [$phaseId] : [];
        while (isset(self::PHASE_TRANSFER[$phaseId])) {
            $phaseId = self::PHASE_TRANSFER[$phaseId];
            $descendingPhases[] = $phaseId;
        }
        return $descendingPhases;
    }

    /**
     * @param $phaseId
     * @return array
     */
    public function getProjectStatusesAtPhase($phaseId)
    {
        $getConfig = config('vars.status_ids_per_phase');
        return $getConfig[$phaseId];
    }

    public function getProjectStatusesSincePhase($phaseId) {
        $descendingPhases = $this->getDescendingPhases($phaseId);
        $projectStatuses = [];
        foreach ($descendingPhases as $phase) {
            $projectStatuses = array_merge($projectStatuses, $this->getProjectStatusesAtPhase($phase));
        }
        return $projectStatuses;
    }
}